﻿
#region Using directives

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Specialized;
using System.Text;

// SMO namespaces
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer;
using Microsoft.SqlServer.MessageBox;
using CodeSmith.Engine;
using SchemaExplorer;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Collections;
using SD.LLBLGen.Pro.ORMSupportClasses;

#endregion

namespace EPN.Tooling.SmithBuilder
{
    partial class TableBuilder : Form
    {
        // Use the Server object to connect to a specific server
        private Server SqlServerSelection;
        Dictionary<string, string> fileMapping = new Dictionary<string, string>();


        public TableBuilder()
        {
            InitializeComponent();
        }

        private void ScriptTable_Load(object sender, System.EventArgs e)
        {
            ServerConnection ServerConn;
            ServerConnect scForm;
            DialogResult dr;

            // Display the main window first
            this.Show();
            Application.DoEvents();

            ServerConn = new ServerConnection();
            scForm = new ServerConnect(ServerConn);
            dr = scForm.ShowDialog(this);
            if ((dr == DialogResult.OK) &&
                (ServerConn.SqlConnectionObject.State == ConnectionState.Open))
            {
                SqlServerSelection = new Server(ServerConn);
                if (SqlServerSelection != null)
                {                   
                    ShowDatabases(true);
                }
            }
            else
            {
                this.Close();
            }
            
            lstTemplate.Items.Clear();
            string[] files=Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.cst");
            lstTemplate.Items.AddRange(files);

            txtTargetFolder.Text = AppDomain.CurrentDomain.BaseDirectory;

           
        }

        private void ScriptTable_FormClosed(object sender, System.EventArgs e)
        {
            if (SqlServerSelection != null)
            {
                if (SqlServerSelection.ConnectionContext.SqlConnectionObject
                    .State == ConnectionState.Open)
                {
                    SqlServerSelection.ConnectionContext.Disconnect();
                }
            }
        }

        private void ScriptTableButton_Click(System.Object sender,
            System.EventArgs e)
        {
            Cursor csr = null;
            Database db;
            StringCollection strColl;
            Scripter scrptr;
            SqlSmoObject[] smoObjects;
            Table tbl;
            Int32 count;

            try
            {
                csr = this.Cursor;   // Save the old cursor
                this.Cursor = Cursors.WaitCursor;   // Display the waiting cursor

                // Use the selected database
                db = (Database)DatabasesComboBox.SelectedItem;
                if (db.Name.Length == 0)
                {
                    ExceptionMessageBox emb = new ExceptionMessageBox();
                    emb.Text = "NoDatabaseSelected";
                    emb.Show(this);                    
                    return;
                }
                string[] tables = rtfTables.Lines;

             
            }
            catch (SmoException ex)
            {
                ExceptionMessageBox emb = new ExceptionMessageBox(ex);
                emb.Show(this);
            }
            finally
            {
                // Clean up
                sbrStatus.Text = "Done";
                // Restore the original cursor
                this.Cursor = csr;
            }
        }

        private void ShowDatabases(bool selectDefault)
        {
            // Show the current databases on the server
            try
            {
                // Clear control
                DatabasesComboBox.Items.Clear();

                // Limit the properties returned to just those that we use
                SqlServerSelection.SetDefaultInitFields(typeof(Database),
                    new String[] { "Name", "IsSystemObject", "IsAccessible" });

                // Add database objects to combobox; the default ToString 
                // will display the database name
                foreach (Database db in SqlServerSelection.Databases)
                {
                    if (db.IsSystemObject == false && db.IsAccessible == true)
                    {
                        DatabasesComboBox.Items.Add(db);
                    }
                }

                if ((selectDefault == true) &&
                    (DatabasesComboBox.Items.Count > 0))
                {
                    DatabasesComboBox.SelectedIndex = 0;
                }
            }
            catch (SmoException ex)
            {
                ExceptionMessageBox emb = new ExceptionMessageBox(ex);
                emb.Show(this);
            }
        }

        private void ShowTables()
        {
            Database db;
            Cursor csr = null;

            try
            {
                csr = this.Cursor;   // Save the old cursor
                this.Cursor = Cursors.WaitCursor;   // Display the waiting cursor

                // Clear the tables list                
                tree.Nodes.Clear();
                

                // Limit the properties returned to just those that we use
                SqlServerSelection.SetDefaultInitFields(typeof(Table),
                    new String[] { "Name", "CreateDate", "IsSystemObject" });

                // Show the current tables for the selected database
                if (DatabasesComboBox.SelectedIndex >= 0)
                {
                    db = (Database)DatabasesComboBox.SelectedItem;
                    string connectionString = SqlServerSelection.ConnectionContext.ConnectionString + ";database=" + db.Name;
                    lblConnectionString.Text = connectionString;

                    TreeNode root = tree.Nodes.Add(db.Name);
                    root.ImageIndex = 0;
                    root.SelectedImageIndex = 0;
                    root.Tag = "Database";

                    foreach (Table tbl in db.Tables)
                    {
                        if (tbl.IsSystemObject == false)
                        {                            
                            TreeNode node = root.Nodes.Add(tbl.Name);
                            node.ImageIndex = 1;
                            node.SelectedImageIndex = 1;
                            node.Tag = "Table";

                        }
                    }                   
                }
            }
            catch (SmoException ex)
            {
                ExceptionMessageBox emb = new ExceptionMessageBox(ex);
                emb.Show(this);
            }
            finally
            {
                this.Cursor = csr;  // Restore the original cursor
            }
        }

        private void Databases_SelectedIndexChangedComboBox(System.Object
            sender, System.EventArgs e)
        {
            ShowTables();
            if (DatabasesComboBox.SelectedIndex >= 0)
            {
                ScriptTableButton.Enabled = true;
            }
            else
            {
                ScriptTableButton.Enabled = false;
            }
        }

        public  void RunTemplate(string templateFile,string connectionString,string tableName,string targetFile)
        {
            CodeTemplateCompiler compiler = new CodeTemplateCompiler(templateFile);
            compiler.Compile();

            if (compiler.Errors.Count == 0)
            {
                CodeTemplate template = compiler.CreateInstance();
              
                DatabaseSchema database = new DatabaseSchema(new SqlSchemaProvider(), connectionString);
                TableSchema tableSchema = database.Tables[tableName];              

                //如果都是字符串，应该可以，要考虑bool,int,object
                Dictionary<string, object> dic = new Dictionary<string, object>();
                string[] paramterValues = rtfParameter.Lines;
                foreach (string parm in paramterValues)
                {
                    if (!String.IsNullOrEmpty(parm))
                    {
                        string[] values = Regex.Split(parm, "=");
                        dic.Add(values[0], values[1]);
                    }
                }
                PropertyInfo[] pis = template.GetProperties();
                foreach (PropertyInfo pi in pis)
                {
                    //参数中hidden的一个，就是表名
                    object val=null;
                    if(dic.TryGetValue(pi.Name,out val))                    
                      template.SetProperty(pi, val);
                    else
                        template.SetProperty(pi, tableSchema);
                }         
                if(File.Exists(targetFile))
                    File.Delete(targetFile);
                using (StreamWriter writer = new StreamWriter(targetFile))
                {
                    template.Render(writer);
                    writer.Flush();                    
                }
            }
            else
            {
                for (int i = 0; i < compiler.Errors.Count; i++)
                {
                    Console.Error.WriteLine(compiler.Errors[i].ToString());
                }
            }
            Console.ReadLine();
        }

        private void tree_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            TreeNode node = tree.GetNodeAt(e.X, e.Y);
            string table = node.Text; 
            string[] nodes = rtfTables.Lines;
            bool exist = false;
            foreach (string tbl in nodes)
                if (tbl.Equals(table, StringComparison.InvariantCultureIgnoreCase))
                    exist = true;
            if (!exist && node.Tag.ToString().Equals("Table"))
                rtfTables.AppendText(table+Environment.NewLine);
        }

        private void lstTemplate_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (String.IsNullOrEmpty(rtfParameter.Text))
            {
                string templateFile = lstTemplate.SelectedItem.ToString();
                CodeTemplateCompiler compiler = new CodeTemplateCompiler(templateFile);
                compiler.Compile();
                rtfParameter.Clear();
                if (compiler.Errors.Count == 0)
                {
                    CodeTemplate template = compiler.CreateInstance();
                    PropertyInfo[] pis = template.GetProperties();
                    foreach (PropertyInfo pi in pis)
                        rtfParameter.AppendText(pi.Name + Environment.NewLine);
                }
            }
        }

        private void ScriptTableButton_Click_1(object sender, EventArgs e)
        {
            sbrStatus.Text = "Working...";

            string templateFile = lstTemplate.SelectedItem.ToString();
            string tmpFile=Path.GetFileNameWithoutExtension(templateFile);
            string targetFileName = string.Empty;
            string AssemblyFile = string.Empty;
            foreach (string table in rtfTables.Lines)
            {

                if (!String.IsNullOrEmpty(table))
                {
                    string entityName;
                    foreach (string parm in rtfParameter.Lines)
                    {
                        if (!String.IsNullOrEmpty(parm) && parm.StartsWith("AssemblyFile"))
                        {
                            string[] values = Regex.Split(parm, "=");
                            AssemblyFile=values[1];
                        }
                    }
                    entityName = EntityClassHelper.GetEntityName(table, AssemblyFile);
                    entityName = EntityClassHelper.TrimEntityName(entityName);
                    if (tmpFile.Contains("Interface"))
                        targetFileName = string.Format("I{0}Manager.cs", entityName);
                    if (tmpFile.Contains("Manager"))
                        targetFileName = string.Format("{0}Manager.cs", entityName);
                    if (tmpFile.Contains("EntryForm"))
                        targetFileName = string.Format("{0}EntryForm.cs", entityName);
                    if (tmpFile.Contains("Validation"))
                        targetFileName = string.Format("{0}Validation.cs", entityName);

                    targetFileName = Path.Combine(txtTargetFolder.Text, targetFileName);
                    
                    RunTemplate(templateFile, lblConnectionString.Text, table, targetFileName);
                }
            }
            sbrStatus.Text = "Done";
        }

        private void tree_AfterCheck(object sender, TreeViewEventArgs e)
        {
            TreeNode node = e.Node;
            if (node.Tag.ToString().Equals("Database"))
            {
                foreach (TreeNode table in node.Nodes)
                {
                    table.Checked = true;

                    string tableName = table.Text;
                    string[] nodes = rtfTables.Lines;
                    bool exist = false;
                    foreach (string tbl in nodes)
                        if (tbl.Equals(tableName, StringComparison.InvariantCultureIgnoreCase))
                            exist = true;
                    if (!exist && node.Tag.ToString().Equals("Table"))
                        rtfTables.AppendText(table + Environment.NewLine);
                }
            }
            else
            {
                string table = node.Text;
                string[] nodes = rtfTables.Lines;
                bool exist = false;
                foreach (string tbl in nodes)
                    if (tbl.Equals(table, StringComparison.InvariantCultureIgnoreCase))
                        exist = true;
                if (!exist && node.Tag.ToString().Equals("Table"))
                    rtfTables.AppendText(table + Environment.NewLine);
            }
        }

        private void btnSelectFolder_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog dlg = new FolderBrowserDialog();
            if (dlg.ShowDialog() == DialogResult.OK)
                txtTargetFolder.Text = dlg.SelectedPath;
        }
        
    }
    public class EntityClassHelper
    {
        
        public static string GetEntityName(string table, string assemblyFile)
        {
            string entity = string.Empty;
            string tableName = string.Empty;
            Assembly assembly = Assembly.LoadFile(assemblyFile);
            if (assembly == null)
                throw new ApplicationException("Load assembly failed");
            Type[] types = assembly.GetTypes();
            foreach (Type type in types)
            {
                if (!string.IsNullOrEmpty(type.Namespace))
                {
                    string nspace = type.Namespace.Substring(type.Namespace.LastIndexOf('.') + 1);
                    if (type.Name.EndsWith("Entity") & nspace == "EntityClasses")
                    {
                        tableName = GetSourceTableName(type.Name, assemblyFile);
                        if (String.Equals(table, tableName, StringComparison.InvariantCultureIgnoreCase))
                        {
                            entity = type.Name;
                            break;
                        }
                    }
                }
            }
            if (entity.Length == 0)
                throw new ApplicationException("Failed to get entity");
            return entity;
        }

        public static string GetSourceTableName(string typeName, string assemblyFile)
        {
            string SourceTableName = string.Empty;
            object temp;
            object target;
            if (File.Exists(assemblyFile))
            {
                Assembly assembly = Assembly.LoadFile(assemblyFile);
                string solution = PrefixProjectName(assembly);
                string fulltypeName = solution + ".DatabaseSpecific.PersistenceInfoProviderSingleton";
                Type type = assembly.GetType(fulltypeName);
                temp = Activator.CreateInstance(type, true);
                target = temp.GetType().InvokeMember("GetInstance", BindingFlags.InvokeMethod, null, temp, null);
                if (target != null)
                {
                    object obj = target.GetType().InvokeMember("GetAllFieldPersistenceInfos", BindingFlags.InvokeMethod, null, target, new string[] { typeName });
                    if (obj != null)
                    {
                       
                        IEnumerator im = (obj as Array).GetEnumerator();
                        while (im.MoveNext())
                        {                          
                            object objp = im.Current as FieldPersistenceInfo;
                            object objname = im.Current.GetType().InvokeMember("SourceObjectName", BindingFlags.GetProperty, null, im.Current, null);

                            SourceTableName = objname.ToString();
                            if (!String.IsNullOrEmpty(SourceTableName))
                                break;
                        }

                    }
                }
            }
            return SourceTableName;
        }

        public static string TrimEntityName(string entityName)
        {
            string entity = string.Empty;
            if (entityName.Contains("Entity"))
            {
                entity = entityName.Substring(0, entityName.Length - 6);
            }
            else
                throw new ApplicationException("");
            return entity;
        }
        private static string PrefixProjectName(Assembly assembly)
        {
            Type[] types = assembly.GetTypes();
            string relation = "";
            string result = "";
            foreach (Type t in types)
            {
                relation = t.FullName;
                int idx = relation.IndexOf(".");
                result = relation.Substring(0, idx + 1);

                relation = relation.Substring(idx + 1);
                idx = relation.IndexOf(".");
                result += relation.Substring(0, idx);
                break;
            }
            return result;
        }
    }
}